package com.tracelog.core.handler;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.tracelog.core.annotation.TraceLog;
import com.tracelog.core.config.TraceLogProperties;
import com.tracelog.core.enums.IncludeType;
import com.tracelog.core.model.LogRecord;
import com.tracelog.core.util.RequestUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.Instant;
import java.util.*;

/**
 * 默认日志处理器实现
 *
 * @author tracelog
 * @since 1.0.0
 */
@Slf4j
@RequiredArgsConstructor
public class DefaultLogHandler implements LogHandler {

    private final TraceLogProperties properties;

    @Override
    public LogRecord createLogRecord(Method method, Class<?> targetClass) {
        // 获取TraceLog注解
        TraceLog methodLog = AnnotationUtils.findAnnotation(method, TraceLog.class);
        TraceLog classLog = AnnotationUtils.findAnnotation(targetClass, TraceLog.class);
        
        // 合并包含的信息类型
        Set<IncludeType> includes = new HashSet<>(properties.getIncludes());
        
        if (methodLog != null && ArrayUtil.isNotEmpty(methodLog.includes())) {
            includes.addAll(Arrays.asList(methodLog.includes()));
        } else if (classLog != null && ArrayUtil.isNotEmpty(classLog.includes())) {
            includes.addAll(Arrays.asList(classLog.includes()));
        }
        
        // 排除指定的信息类型
        if (methodLog != null && ArrayUtil.isNotEmpty(methodLog.excludes())) {
            includes.removeAll(Arrays.asList(methodLog.excludes()));
        } else if (classLog != null && ArrayUtil.isNotEmpty(classLog.excludes())) {
            includes.removeAll(Arrays.asList(classLog.excludes()));
        }
        
        // 创建日志记录
        LogRecord logRecord = LogRecord.create(Instant.now(), includes);
        
        // 设置类名和方法名
        logRecord.setClassName(targetClass.getName());
        logRecord.setMethodName(method.getName());
        
        // 设置日志级别
        if (methodLog != null) {
            logRecord.setLevel(methodLog.level());
        } else if (classLog != null) {
            logRecord.setLevel(classLog.level());
        }
        
        // 设置描述和模块
        if (methodLog != null && StrUtil.isNotBlank(methodLog.value())) {
            logRecord.setDescription(methodLog.value());
        }
        
        if (methodLog != null && StrUtil.isNotBlank(methodLog.module())) {
            logRecord.setModule(methodLog.module());
        } else if (classLog != null && StrUtil.isNotBlank(classLog.module())) {
            logRecord.setModule(classLog.module());
        }
        
        // 合并敏感字段列表
        Set<String> sensitiveFields = new HashSet<>(properties.getSensitiveFields());
        if (methodLog != null && ArrayUtil.isNotEmpty(methodLog.sensitiveFields())) {
            sensitiveFields.addAll(Arrays.asList(methodLog.sensitiveFields()));
        }
        logRecord.setSensitiveFields(sensitiveFields);
        
        return logRecord;
    }

    @Override
    public void processRequestInfo(LogRecord logRecord) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        
        HttpServletRequest request = attributes.getRequest();
        Set<IncludeType> includes = new HashSet<>(properties.getIncludes());
        
        // 记录请求方法和URI
        logRecord.setMethod(request.getMethod());
        logRecord.setUri(request.getRequestURI());
        
        // 记录客户端IP
        if (includes.contains(IncludeType.IP_ADDRESS)) {
            logRecord.setClientIp(getClientIp(request));
        }
        
        // 记录用户代理信息
        if (includes.contains(IncludeType.USER_AGENT)) {
            String userAgent = request.getHeader("User-Agent");
            logRecord.setUserAgent(userAgent);
            
            // TODO: 解析浏览器和操作系统信息
            // 在实际项目中可以使用UserAgentUtils等工具解析
        }
        
        LogRecord.RequestInfo requestInfo = logRecord.getRequest();
        if (requestInfo != null) {
            // 记录请求头
            if (includes.contains(IncludeType.REQUEST_HEADERS)) {
                Map<String, String> headers = new HashMap<>();
                Enumeration<String> headerNames = request.getHeaderNames();
                while (headerNames.hasMoreElements()) {
                    String headerName = headerNames.nextElement();
                    String headerValue = request.getHeader(headerName);
                    
                    // 对敏感信息进行脱敏
                    if (properties.isMaskSensitiveInfo() && isSensitiveHeader(headerName)) {
                        headerValue = "******";
                    }
                    
                    headers.put(headerName, headerValue);
                }
                requestInfo.setHeaders(headers);
            }
            
            // 记录请求参数
            if (includes.contains(IncludeType.REQUEST_PARAM)) {
                requestInfo.setParameters(request.getParameterMap());
            }
            
            // 记录请求体
            if (includes.contains(IncludeType.REQUEST_BODY)) {
                try {
                    // 检查是否为JSON请求
                    if (RequestUtils.isJsonRequest(request)) {
                        String requestBody = RequestUtils.getRequestBody(request);
                        if (StrUtil.isNotBlank(requestBody)) {
                            // 处理请求体中的敏感信息
                            Object processedBody = processSensitiveInfo(requestBody, logRecord.getSensitiveFields());
                            requestInfo.setBody(processedBody);
                        }
                    } else if (RequestUtils.isMultipartRequest(request)) {
                        // 文件上传请求不记录请求体
                        requestInfo.setBody("Multipart request (file upload)");
                    }
                } catch (Exception e) {
                    log.warn("Failed to process request body", e);
                    requestInfo.setBody("Failed to process request body: " + e.getMessage());
                }
            }
        }
    }

    @Override
    public void processResponseInfo(LogRecord logRecord, Object returnValue) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        
        HttpServletResponse response = attributes.getResponse();
        Set<IncludeType> includes = new HashSet<>(properties.getIncludes());
        
        LogRecord.ResponseInfo responseInfo = logRecord.getResponse();
        if (responseInfo != null && response != null) {
            // 记录状态码
            responseInfo.setStatus(response.getStatus());
            
            // 记录响应头
            if (includes.contains(IncludeType.RESPONSE_HEADERS)) {
                Map<String, String> headers = new HashMap<>();
                Collection<String> headerNames = response.getHeaderNames();
                for (String headerName : headerNames) {
                    headers.put(headerName, response.getHeader(headerName));
                }
                responseInfo.setHeaders(headers);
            }
            
            // 记录响应体
            if (includes.contains(IncludeType.RESPONSE_BODY) && returnValue != null) {
                try {
                    // 检查响应大小
                    String jsonBody = JSONUtil.toJsonStr(returnValue);
                    if (jsonBody.length() > properties.getLargeObjectThreshold()) {
                        responseInfo.setBody("Large response object, size: " + jsonBody.length() + " bytes");
                    } else {
                        responseInfo.setBody(processSensitiveInfo(returnValue, logRecord.getSensitiveFields()));
                    }
                } catch (Exception e) {
                    responseInfo.setBody("Failed to serialize response: " + e.getMessage());
                    log.warn("Failed to serialize response", e);
                }
            }
        }
    }

    @Override
    public void processExceptionInfo(LogRecord logRecord, Throwable e) {
        if (e != null) {
            logRecord.setExceptionMessage(e.getClass().getName() + ": " + e.getMessage());
            
            // 记录异常堆栈，最多记录前10行
            if (e.getStackTrace() != null && e.getStackTrace().length > 0) {
                StringBuilder stackTrace = new StringBuilder();
                int maxStackLines = Math.min(e.getStackTrace().length, 10);
                
                for (int i = 0; i < maxStackLines; i++) {
                    stackTrace.append("    at ").append(e.getStackTrace()[i]).append("\n");
                }
                
                logRecord.setExceptionStackTrace(stackTrace.toString());
            }
        }
    }

    @Override
    public boolean shouldLog(Method method, Class<?> targetClass) {
        // 非Web请求不记录
        if (RequestContextHolder.getRequestAttributes() == null) {
            return false;
        }
        
        // 获取请求URI
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String uri = request.getRequestURI();
        
        // 检查是否匹配排除模式
        if (properties.isUriExcluded(uri)) {
            return false;
        }
        
        // 检查类和方法上的TraceLog注解
        TraceLog methodLog = AnnotationUtils.findAnnotation(method, TraceLog.class);
        TraceLog classLog = AnnotationUtils.findAnnotation(targetClass, TraceLog.class);
        
        // 如果方法上有TraceLog注解且ignore=true，则不记录
        if (methodLog != null && methodLog.ignore()) {
            return false;
        }
        
        // 如果类上有TraceLog注解且ignore=true，且方法上没有注解，则不记录
        if (classLog != null && classLog.ignore() && methodLog == null) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 获取客户端真实IP
     *
     * @param request HTTP请求
     * @return IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StrUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值，第一个IP才是真实客户端IP
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        
        ip = request.getHeader("X-Real-IP");
        if (StrUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        
        return request.getRemoteAddr();
    }
    
    /**
     * 检查是否为敏感请求头
     *
     * @param headerName 请求头名称
     * @return 是否为敏感请求头
     */
    private boolean isSensitiveHeader(String headerName) {
        String lowercaseHeader = headerName.toLowerCase();
        return lowercaseHeader.contains("token") || 
               lowercaseHeader.contains("auth") || 
               lowercaseHeader.contains("secret") || 
               lowercaseHeader.contains("key") || 
               lowercaseHeader.contains("pwd") || 
               lowercaseHeader.contains("password");
    }
    
    /**
     * 处理敏感信息
     *
     * @param obj 对象
     * @param sensitiveFields 敏感字段列表
     * @return 处理后的对象
     */
    private Object processSensitiveInfo(Object obj, Set<String> sensitiveFields) {
        if (!properties.isMaskSensitiveInfo() || obj == null || sensitiveFields.isEmpty()) {
            return obj;
        }
        
        try {
            // 如果是字符串，先尝试解析为JSON对象
            if (obj instanceof String) {
                String jsonStr = (String) obj;
                try {
                    // 尝试解析为JSON对象
                    cn.hutool.json.JSONObject jsonObj = JSONUtil.parseObj(jsonStr);
                    // 处理敏感字段
                    sensitiveFields.forEach(field -> {
                        if (jsonObj.containsKey(field)) {
                            jsonObj.set(field, "******");
                        }
                    });
                    return jsonObj;
                } catch (Exception e) {
                    // 如果解析失败，可能不是JSON格式，直接返回原字符串
                    return obj;
                }
            } else {
                // 将对象转换为JSONObject
                cn.hutool.json.JSONObject jsonObj = JSONUtil.parseObj(obj);
                
                // 对敏感信息进行脱敏
                sensitiveFields.forEach(field -> {
                    if (jsonObj.containsKey(field)) {
                        jsonObj.set(field, "******");
                    }
                });
                
                // 如果原对象是普通的JavaBean，转换回相同类型
                if (obj.getClass().getPackage().getName().startsWith("com.tracelog")) {
                    return jsonObj.toBean(obj.getClass());
                }
                
                return jsonObj;
            }
        } catch (Exception e) {
            log.warn("Failed to process sensitive information", e);
            return obj;
        }
    }
} 